package com.hexb.smh.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.googlecode.aviator.AviatorEvaluator;
import com.hexb.core.exception.BusinessException;
import com.hexb.core.model.BaseErrorCodes;
import com.hexb.core.utils.Assert;
import com.hexb.core.utils.MapBuilder;
import com.hexb.core.utils.ObjectUtils;
import com.hexb.smh.entity.Config;
import com.hexb.smh.entity.enums.ConfigType;
import com.hexb.smh.entity.param.config.ExpressionValidParam;
import com.hexb.smh.errors.ErrorCodes;
import com.hexb.smh.mapper.*;
import com.hexb.smh.service.IConfigService;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.List;
import java.util.Map;

/**
 * @author : hexb
 */
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@CacheConfig(cacheNames = {"config"})
@Slf4j
public class ConfigService implements IConfigService {

    static private final DecimalFormat f = new DecimalFormat("#.##");

    private final static ObjectMapper MAPPER = new ObjectMapper();

    static final public Map<String, Object> defaultCtx = MapBuilder
            .<String, Object>create("A", new BigDecimal("81.2"))
            .build("B", new BigDecimal("72.8"))
            .build("C", new BigDecimal("83.1"))
            .build("D", new BigDecimal("4.0"))
            .build("E", new BigDecimal("0.15"))
            .build("F", new BigDecimal("500"))
            .map();

    @NonNull
    final private ConfigMapper configMapper;

    @Override
    @CacheEvict(allEntries = true)
    public int saveOrUpdate(@NotNull Config config) {
        Assert.isNull(config, BaseErrorCodes.PARAMETER_IS_NULL);
        if (config.getType() == ConfigType.expression) {
            this.validExpression(config.getValue(), null);
        }
        return configMapper.saveOrUpdate(config);
    }

    @Override
    @CacheEvict(allEntries = true)
    public int saveBatch(@NotNull List<Config> configList) {
        Assert.isEmpty(configList, BaseErrorCodes.PARAMETER_IS_NULL);
        return configMapper.saveBatch(configList);
    }

    @Override
    @Cacheable(value = "config", key = "targetClass + methodName")
    public List<Config> list() {
        return configMapper.read();
    }

    @Override
    @Cacheable(value = "config", key = "targetClass + methodName")
    public Map<ConfigType, Config> map() {
        List<Config> list = this.list();
        return ObjectUtils.list2map(list, Config::getType);
    }

    @Override
    public String validExpression(String expression, ExpressionValidParam param) {
        try {
            Object evaluate = AviatorEvaluator.execute(expression, param != null ? param.toContext() : defaultCtx);
            Assert.isNull(evaluate, ErrorCodes.INVALID_EXPRESSION);
            return evaluate.toString();
        } catch (Exception e) {
            throw new BusinessException(ErrorCodes.INVALID_EXPRESSION);
        }
    }

}
